#pragma once

#include<iostream>
#include<string>
#include<queue>  
#include<pthread.h>

static __uint32_t gcap=5;//防止bug;


template<typename T>
class BlockQueue
{
private:
    bool isFull()
    {return _bp.size>=_cap;}
    bool isEmpty()
    {return _bp.empty();}

public:

    BlockQueue(__uint32_t cap=gcap): _cap(cap),
    {
        pthread_mutex_init(&lock,nullptr);
        pthread_cond_init(&_c_cond,nullptr);
        pthread_cond_init(&_p_cond,nullptr);
    }

    void Enqueue(T &in)
    {
        pthread_mutex_lock(&_lock);//上锁
        //判断是否可以生产，判断也是在访问临界资源，需要在临界区内
        if(isFull())
        {
            //满了需要等待
            //等待时需要释放锁
            _p_wait_num++;

            pthread_cond_wait(&_p_cond,&_lock);
            //so 特征1：释放锁；特征2：重新竞争锁。即wait函数一半释放锁，另一半在接收到信号后重新竞争锁
            //so 当我们被唤醒，就是在这个位置，临界区的内部被唤醒！！！！！

            _p_wait_num--;

        }
        _bp.push(in);

        if(_c_wait_num>0)
        pthread_cond_signal(&_c_cond);//此时若有等待的消费者，可以通知他来取货，有货了

        pthread_mutex_unlock(&lock);
    }
    void PopQueuq(T* out) //传入用*，传进用&，又需传出和传入的也用&；
    {
        pthread_mutex_lock(&lock);
        if(&isEmpty())
        {
            _c_wait_num++;
            pthread_cond_wait(&_c_cond.&_lock);
            _c_wait_num--;
        }

        *out=_bp.front()
        _bp.pop();

        if(_c_wait_num>0) //此时有消费者正在等待，说明货不够了，唤醒生产者
        pthread_cond_signal(&_p_cond);

        pthread_mutex_unlock(&_lock);
    }


private:
    std::queue<T> _bp;
    __uint32_t _cap;

    pthread_mutex_t _lock;
    pthread_cond_t _c_cond;
    pthread_cond_t _p_cond;

    int _c_wait_num; // 当前消费者等待的个数
    int _p_wait_num; // 当前生产者等待的个数
};
